package CI;

import java.util.ArrayList;

public class Primitives {

	public static int primitivePlus (ArrayList<Object> operands) throws EvalError
	{
		if (operands.size() == 0)
			return 0;
		else {
			int first = 0;
			if (operands.get(0) instanceof Integer) {
				first = (Integer)operands.remove(0);
			}
			else {
				throw new EvalError("Attempt to add none-integer: " + operands.get(0).toString());
			}	
			return first + primitivePlus (operands);
		}
	}

	public static int primitiveTimes (ArrayList<Object> operands) throws EvalError
	{
		if (operands.size() == 0)
			return 0;
		else
		{
			int first = 0;
			if (operands.get(0) instanceof Integer) {
				first = (Integer)operands.remove(0);
			}
			else {
				throw new EvalError("Attempt to multiply none-integer: " + operands.get(0).toString());
			}
			return first * primitivePlus (operands);
		}
	}

	public static int primitiveMinus (ArrayList<Object> operands) throws EvalError
	{
		if (operands.size() == 1)
		{			
			if (operands.get(0) instanceof Integer) {
				return -1 * (Integer)operands.get(0);
			}
			else {
				throw new EvalError("Attempt to subtract none-number: " + operands.get(0).toString());
			}	
		}
		else if(operands.size() == 2)
		{
			if (operands.get(0) instanceof Integer && operands.get(1) instanceof Integer) {
				return (Integer)operands.get(0) - (Integer)operands.get(1);
			}
			else {
				throw new EvalError("Attempt to subtract none-number");
			}
		}
		else
			throw new EvalError(String.format("- expects 1 or 2 operands, given %s: %s", operands.size(), 
					operands.toString()));
	}
	public static void checkOperands(ArrayList<Object> operands, int num, String prim) throws EvalError
	{
		if (operands.size() != num)
		{
			throw new EvalError(String.format("Primitive %s expected %s operands, given %s: %s", 
					prim, num, operands.size(), operands.toString()));
		}
	}

	public static Boolean primitiveEquals (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 2, "=");
		return (operands.get(0) == operands.get(1));
	}

	public static Boolean primitiveZero (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 1, "zero?");

		int temp = (Integer)operands.get(0); 
		return (temp == 0);
	}
	public static Boolean primitiveGreater (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 2, ">");
		int temp0 = (Integer)operands.get(0);
		int temp1 = (Integer)operands.get(1);
		return (temp0 > temp1);
	}
	public static Boolean primitiveLessThan (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 2, "<");
		int temp0 = (Integer)operands.get(0);
		int temp1 = (Integer)operands.get(1);

		return (temp0 < temp1);
	}

	public static Boolean primitiveLessThanEquals (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 2, "<=");
		int temp0 = (Integer)operands.get(0);
		int temp1 = (Integer)operands.get(1);

		return (temp0 <= temp1);
	}
	public static Object primitiveCons (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 2, "cons");
		return new ConsCell(operands.get(0), operands.get(1));
	}

	public static Object primitiveCar (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 1, "car");
		ConsCell temp = (ConsCell)operands.get(0);
		return temp.getFirst();
	}

	public static Object primitiveCdr (ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 1, "cdr");
		ConsCell temp = (ConsCell)operands.get(0);
		return temp.getSecond();
	}
	public static Boolean primitiveNullTest(ArrayList<Object> operands) throws EvalError
	{
		checkOperands (operands, 1, "null?");
		return (operands.get(0) == null);
	}
	public static Object primitiveList(ArrayList<Object> operands)
	{
		if (operands.size() == 0)
			return null;
		else
		{
			Object first = operands.get(0);
			operands.remove(0);
			return new ConsCell(first, primitiveList(operands));
		}
	}
}
